﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Windows.Forms;
using EasyStep.Business;
using EasyStep.Business.Constants;
using EasyStep.Business.Interfaces;
using EasyStep.Common;
using EasyStep.TestEditor;
using EasyStep.TestEditor.Test;
using EasyStep.WinForms.Test;


namespace EasyStep.WinForms
{
    public static class Program
    {
        [STAThread]
        public static void Main(string[] args)
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Idle += new EventHandler(Application_Idle);
            try
            {
                Initializer(args);
                Form form = null;

                switch (StartForm)
                {
                    case ShowFormType.Editor:
                        form = new Edit(ContextInstance, out onIdle);
                        break;
                    case ShowFormType.Runner:
                        form = new Execute(ContextInstance, out onIdle);
                        break;
                    case ShowFormType.History:
                        form = new History(ContextInstance, out onIdle);
                        break;
                    default:
                        form = new Edit(ContextInstance, out onIdle);
                        break;
                }

                ContextInstance.Log.Debug("Show form:" + StartForm.ToString());
                Application.Run(form);
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
                ContextInstance.Log.Debug("Error:" + ex.Message);
                ContextInstance.Log.Fatal(ex);

                //throw new ApplicationException("Fatal unhandled Exception", ex);
            }
            Application.Idle -= Application_Idle;
        }

        private static void Initializer(string[] args)
        {
            var settings = new EasyStepSettings();

            var pathReplacements = new Dictionary<string, string>();
            try
            {
                pathReplacements.Add("%root%", AppDomain.CurrentDomain.BaseDirectory);
                pathReplacements.Add("%appdata%", Application.UserAppDataPath);
                pathReplacements.Add("%temp%", System.IO.Path.GetTempPath());
            }
            catch (System.Security.SecurityException secEx)
            {
                MessageBox.Show(secEx.Message, "Error!", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }

            pathReplacements.Add(MainApplicationServiceConstants.FileSystemDataStoragePath, (settings.ResultsFolder ?? "%appdata%\\results\\").TransformReplacements(pathReplacements));

            ObjectManager.Initialize();
            ObjectManager.RegisterInstance<ITestDataContext>(() => new TestDataContext(AppDomain.CurrentDomain.BaseDirectory));
            ContextInstance = ObjectManager.GetInstance<ITestDataContext>();
            ContextInstance.Environment = string.IsNullOrEmpty(settings.Environment) ? "" : settings.Environment;
            ContextInstance.PathReplacements = pathReplacements;
            EnsureLogExist();
            if (!Debugger.IsAttached && settings.AttachDebugger) EnterDebugMode();
            string actionsPath = settings.TestActionsAssembliesPath.TransformReplacements(pathReplacements);
            ContextInstance.Log.Debug("Read TestAction List from assemblies by path:" + actionsPath);            
            ContextInstance.Log.Info("Application is working under Environment:" + ContextInstance.Environment);
            ContextInstance.Log.Info("Initialize Application services");
            EasyStep.Business.MainApplicationService.Initialize(pathReplacements);
            TypeFinder.Initialize(actionsPath);            
            if (!ContextInstance.ActionManager.GetAll().Any())
                throw new NotSupportedException("Can't continue, no one Action been found to work with tests.\r\nCheck that folder contains *.dll with actions:\r\n" + actionsPath);

            
            RegisterWebDrivers();
            InitTestFromCommandLine(args);
        }

        private static void EnsureLogExist()
        {
            if (ContextInstance.Log == null) MessageBox.Show("Null log4net Log object!Check config!");
            else
            {//check log
                var l = ContextInstance.Log.Logger as log4net.Repository.Hierarchy.Logger;
                if (l != null)
                {
                    if (l.Appenders.Count == 0)
                        MessageBox.Show("No one appender have been found!Check config!");
                }
                else
                    MessageBox.Show("Null Logger -check config!");
            }
        }

        private static void EnterDebugMode()
        {
            try
            {
                ContextInstance.Log.Debug("Attaching Debugger...");
                Debugger.Launch();
            }
            catch (Exception ex)
            {
                ContextInstance.Log.Debug("Error!Can't Attach Debugger", ex);
            }
        }

        private static void RegisterWebDrivers()
        {
            try
            {
                var browserTypes = TypeFinder.GetDerivedFrom<EasyStep.Business.Interfaces.IWebBrowserProxy>();
                var browsers = new List<IWebBrowserProxy>();
                foreach (Type browser in browserTypes)
                {
                    var bInfo = Activator.CreateInstance(browser) as IWebBrowserProxy;
                    bInfo.Icon = string.Concat("%root%\\resources\\", bInfo.Icon ?? "").TransformReplacements(ContextInstance.PathReplacements);
                    //bInfo.Description=??get string data from resource file
                    browsers.Add(bInfo);
                }
                ContextInstance.RegisteredBrowsers = browsers.ToArray();
                ContextInstance.Log.Debug(string.Format("Have been registered: {0}-browser types", ContextInstance.RegisteredBrowsers == null ? -1 : ContextInstance.RegisteredBrowsers.Length));
            }
            catch (Exception ex)
            {
                var e = new InvalidDataException("Can't register browsers to test, check that  classes which derived from IWebBrowserProxy are correct.", ex);
                ContextInstance.Log.Error("Error, can't continue without IWebBrowserDriver", e);
                throw e;
            }
        }

        public static void InitTestFromCommandLine(string[] mainArgs)
        {
            var commandArguments = mainArgs;
            if (commandArguments == null || commandArguments.Length == 0)
            {
                commandArguments = Environment.GetCommandLineArgs().Skip(1).ToArray();
            }
            if (commandArguments== null || commandArguments.Length == 0) return;

            if (!Debugger.IsAttached)
            {//enable/disable debugger;
                foreach (var s in commandArguments)
                    if ("debug".Equals(s, StringComparison.OrdinalIgnoreCase))
                        EnterDebugMode();

            }
            //automatically set test file
            var CurrentTest = commandArguments[0];
            if (!Path.IsPathRooted(CurrentTest) && !CurrentTest.StartsWith("%root%"))
            {
                CurrentTest = string.Concat("%root%", CurrentTest);
            }
            CurrentTest = CurrentTest.TransformReplacements(ContextInstance.PathReplacements);
            ContextInstance.CurrentProject = Path.GetDirectoryName(CurrentTest);
            ContextInstance.CurrentTest = Path.GetFileName(CurrentTest);
            ContextInstance.Log.Debug("Automatically open test file:" + CurrentTest);


            if (commandArguments.Where(a => a.Equals("/r", StringComparison.OrdinalIgnoreCase)).Any())
            {
                StartForm = ShowFormType.Runner;
                ContextInstance.Log.Debug("Automatically open Form: TestExecutor");
            }
            else if (commandArguments.Where(a => a.Equals("/h", StringComparison.OrdinalIgnoreCase)).Any())
            {
                StartForm = ShowFormType.History;
                ContextInstance.Log.Debug("Automatically open Form: TestHistory");
            }
            else
            {
                StartForm = ShowFormType.Editor;
                ContextInstance.Log.Debug("Automatically open Form: TestEditor");
            }
        }
        #region Help methods
        public static Action onIdle = null;
        static void Application_Idle(object sender, EventArgs e)
        {
            uint idleTime = GetIdleTime();
            if (idleTime > 1000 && onIdle != null)
            {
                onIdle();
            }
        }
        public static uint GetIdleTime()
        {
            LASTINPUTINFO LastUserAction = new LASTINPUTINFO();
            LastUserAction.cbSize = (uint)System.Runtime.InteropServices.Marshal.SizeOf(LastUserAction);
            GetLastInputInfo(ref LastUserAction);
            return ((uint)Environment.TickCount - LastUserAction.dwTime);
        }



        [DllImport("User32.dll")]
        private static extern bool GetLastInputInfo(ref LASTINPUTINFO Dummy);
        [DllImport("Kernel32.dll")]
        private static extern uint GetLastError();


        public static DialogResult InputBox(string title, string promptText, ref string value)
        {
            Form form = new Form();
            Label label = new Label();
            TextBox textBox = new TextBox();
            Button buttonOk = new Button();
            Button buttonCancel = new Button();

            form.Text = title;
            label.Text = promptText;
            textBox.Text = value;

            buttonOk.Text = "OK";
            buttonCancel.Text = "Cancel";
            buttonOk.DialogResult = DialogResult.OK;
            buttonCancel.DialogResult = DialogResult.Cancel;

            label.SetBounds(9, 20, 372, 13);
            textBox.SetBounds(12, 36, 372, 20);
            buttonOk.SetBounds(228, 72, 75, 23);
            buttonCancel.SetBounds(309, 72, 75, 23);

            label.AutoSize = true;
            textBox.Anchor = textBox.Anchor | AnchorStyles.Right;
            buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;
            buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right;

            form.ClientSize = new Size(396, 107);
            form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel });
            form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height);
            form.FormBorderStyle = FormBorderStyle.FixedDialog;
            form.StartPosition = FormStartPosition.CenterScreen;
            form.MinimizeBox = false;
            form.MaximizeBox = false;
            form.AcceptButton = buttonOk;
            form.CancelButton = buttonCancel;

            DialogResult dialogResult = form.ShowDialog();
            value = textBox.Text;
            return dialogResult;
        }

        private static ShowFormType StartForm;
        private static ITestDataContext ContextInstance;

        #endregion
    }

    struct LASTINPUTINFO
    {
        public uint cbSize;

        public uint dwTime;
    }
    enum ShowFormType
    {
        Editor = 0,
        Runner = 1,
        History = 2
    }
}
